#include "RDPTB.h"

#define DIFF( TYPE, VALUE, CODE )\
    if (node->type == TYPE && node->info == VALUE &&\
        node->left && node->right)\
    {\
        CODE;\
    }

tree_t* Diff (root_t* tree, tree_t* node)
{
    ASSERT (tree); ASSERT (node);

    printf ("%d\n", node->type);
    fprintf (The_Log, "---------------------------------------\n");

//    Tree_Dump (stdout, tree);
    Node_Dump (stdout, node, 0);
//    getchar();
//    printf ("DIFF!!\n");

    tree_t* new_node = NULL;

    Node_Construct (&new_node, 0);

    if (node->type == DIFF_TYPE)
    {
        if (node->parent->left == node)
        {
            node->parent->left = NULL;
            Node_Add_Left (tree, node->parent, node->left);
        }

        if (node->parent->right == node)
        {
            node->parent->right = NULL;
            Node_Add_Right (tree, node->parent, node->left);
        }

        node = Node_Copy (Diff (tree, Node_Copy (node->left)));

        return node;
    }

    if (node->type == NUMBER)
    {
        fprintf (stdout, "NUM!\n");
        node->info = 0;
        node->left = NULL;
        node->right = NULL;
        node->type = NUMBER;

        return node;
    }

    if (node->type == VARIABLE)
    {
        fprintf (stdout, "X!\n");
        node->type = NUMBER;
        node->left = NULL;
        node->right = NULL;
        node->info = 1;

        return node;
    }

    if (node->type == OPERATION)
    {
        if (node->right == NULL)
        {
            if (node->info == ADD)
            {
//                Node_Dump (stdout, Diff (tree, Node_Copy (node->left)), 0);

                return Diff (tree, Node_Copy (node->left));
            }

            if (node->info == SUB)
            {
                new_node->info = SUB;

                Node_Add_Left (tree, new_node, Diff (tree, Node_Copy (node->left)));

                new_node->type = OPERATION;
//                new_node->left = NULL;
                new_node->right = NULL;

                return node;
            }
        }

        DIFF (OPERATION, ADD,
        return Node_Copy (Create_Op_Node (tree, ADD, Diff (tree, Node_Copy (node->left)), Diff (tree, Node_Copy (node->right)))));

        DIFF (OPERATION, SUB,
        return node = Create_Op_Node (tree, SUB, Diff (tree, node->left), Diff (tree, node->right)););

        if (node->type == OPERATION && node->info == MUL)
        {
            Node_Dump (stdout, node, 0);

            tree_t* node_mul_l = NULL;
            tree_t* node_mul_r = NULL;
            tree_t* node_add = NULL;

            Node_Construct (&node_mul_l, 0);
            Node_Construct (&node_mul_r, 0);
            Node_Construct (&node_add, 0);

            fprintf (stdout, "FIRST:\n");

            node_mul_l = Create_Op_Node (tree, MUL, Diff (tree, Node_Copy (node->left)), Node_Copy (node->right));
//            getchar();
            Node_Dump (stdout, node_mul_l, 0);

            fprintf (stdout, "SECOND:\n");

            node_mul_r = Create_Op_Node (tree, MUL, Node_Copy (node->left), Diff (tree, Node_Copy (node->right)));
//            getchar();
            Node_Dump (stdout, node_mul_r, 0);
            fprintf (stdout, "ADD:\n");

            node_add = Create_Op_Node (tree, ADD, node_mul_l, node_mul_r);
            Node_Dump (stdout, node_add, 0);
            fprintf (stdout, "Endofdiff\n");

            return node = node_add;
        }

        if (node->type == OPERATION && node->info == DIV)
        {
            tree_t* n_m_1 = NULL;
            tree_t* n_m_2 = NULL;
            tree_t* n_m_3 = NULL;
            tree_t* n_s = NULL;
            tree_t* n_d = NULL;

            Node_Construct (&n_m_1, 0);
            Node_Construct (&n_m_2, 0);
            Node_Construct (&n_m_3, 0);
            Node_Construct (&n_s, 0);
            Node_Construct (&n_d, 0);

            n_m_1 = Create_Op_Node (tree, MUL, Diff (tree, Node_Copy (node->left)), Node_Copy (node->right));
            n_m_2 = Create_Op_Node (tree, MUL, Diff (tree, Node_Copy (node->right)), Node_Copy (node->left));
            n_m_3 = Create_Op_Node (tree, MUL, Node_Copy (node->right), Node_Copy (node->right));
            n_s = Create_Op_Node (tree, SUB, n_m_1, n_m_2);
            n_d = Create_Op_Node (tree, DIV, n_s, n_m_3);

            return node = n_d;
         }
    }

//    Node_Print_Dot ()

    fprintf (stdout, "End of Diff\n");

    return node;
}

tree_t* Create_Op_Node (root_t* tree, int op, tree_t* left, tree_t* right)
{
    ASSERT (left); ASSERT (right); ASSERT (tree);

//    printf ("Op_Node: %d\n", op);
//    Node_Dump (stdout, left, 0);
//    Node_Dump (stdout, right, 0);

    tree_t* new_node = NULL;

    Node_Construct (&new_node, 0);

    new_node->info = op;

    Node_Add_Left (tree, new_node, left);
    Node_Add_Right (tree, new_node, right);

    new_node->type = OPERATION;

    return new_node;
}
